<?php

require_once 'Processing/Classes/Application.php';
require_once 'Processing/Classes/TimeGroup.php';
require_once 'Processing/Classes/InformationSystemGroup.php';
/**
 * Group application data to subdivisions.
 * 
 * @link https://docs.google.com/drawings/d/14j1dcTZkUgCa9MAxL90maDSMCHrWQrcpkxXTWGf79qk/edit?hl=lt
 * @status TESTED in (rev123), NOT_APPROVED
 * @author Aurelijus Banelis
 */
class SubdivisionData {
    
    /**
     * @return  [#row][#value_type] (date, measure_code, administrated)
     *          numeric 2D array, ordered  by measure_code, date
     * 
     * @todo where conditions
     * @todo optimization, serializise or etc
     */
    public static function counts_to_dataset() {
        $sqlResult = DB::query("SELECT `date`, measure_code, administrated FROM historical_quantities ORDER BY measure_code, `date`;");
        $quantities = array();
        while ($row = DB::row($sqlResult)) {
            $row[1] = strtolower($row[1]);
            $row[2] = intval($row[2]);
            $quantities[] = $row;
        }
        return $quantities;
    }
    
     /**
     * @return  [#row][#value_type] (date, measure_code, administrated)
     *          numeric 2D array, ordered  by measure_code, date
     * 
     * @todo where conditions
     * @todo optimization, serializise or etc
     */
    public static function counts_to_dataset_for_subdivision($subdivision_code) {
        $subdivision_code = mysql_real_escape_string($subdivision_code);
        $sqlResult = DB::query("
            SELECT hq.`date`, hq.measure_code, hq.administrated 
            FROM historical_quantities AS hq, support_administration AS sa 
            WHERE hq.measure_code = sa.measure_code AND sa.subdivision_code = '".$subdivision_code."' 
            ORDER BY hq.measure_code, hq.`date`            
        ");
        $quantities = array();
        while ($row = DB::row($sqlResult)) {
            $row[1] = strtolower($row[1]);
            $row[2] = floatval($row[2]);
            $quantities[] = $row;
        }
        return $quantities;
    }

    /**
     * @param array $measures  [code]=>name
     * @param array $dates [#]=>date
     * 
     * @return array [measure][date] => TimeGroup
     */
    public static function get_hystorical_quantities($measures, $dates) {
        /* Creating empty list */
        $quantities = array();
        foreach ($measures as $measure_code=>$measure) {
            foreach ($dates as $date) {
                $quantities[$measure_code][$date] = new TimeGroup($date);
                $quantities[$measure_code][$date]->predicted = true;
            }
        }
        
        /* Adding data */
        $dataset = self::counts_to_dataset();
        foreach ($dataset as $row) {
            list($date, $measure_code, $administrated) = $row;
            $quantities[$measure_code][$date]->sum_administrated = $administrated;
            $quantities[$measure_code][$date]->predicted = false;
        }
        return $quantities;
    }
    
    /**
     * @param arrray    $dataset [#row][#value_type] (date, measure_code, administrated)
     *                  numeric 2D array, ordered  by measure_code, date ASCENDING
     * @param array     $subdivisions [code]=>name
     * @param array     $predicted_dates [date]=>boolean is predicted
     * @param array     $is_time_groups [subdivision][date] => InformationSystemGroup
     *                  variable will be filled with results
     * @param array     $measures_subdivisions [measure_code][subdivision_code] => time
     *
     * @return array    [subdivision][date]=>TimeGroup
     */
    public static function dataset_to_table($dataset, $subdivisions, $predicted_dates, &$is_time_groups, $measures_subdivisions = null) {
        /* Creating empty array */
        $time_groups = array();
        foreach ($subdivisions as $code=>$name) {
            foreach ($predicted_dates as $date=>$is_predicted) {
                $time_groups[$code][$date] = new TimeGroup($date, $is_predicted, $code);
            }
        }
        if ($measures_subdivisions == null) {
            $measures_subdivisions = self::get_measures_subdivisions();
        }
        
        /* Information systems */
        $is_time_groups = array();
        //TODO: clean up: do not use db functions here, transfer via function's arguments
        $is_names = get_is_associative();
        foreach ($is_names as $code=>$value) {
            foreach ($predicted_dates as $date=>$is_predicted) {
                $is_time_groups[$code][$date] = new InformationSystemGroup($date, $code, $is_predicted);
            }
        }
        $subdivision_is = get_subdivision_is();
        foreach ($subdivisions as $code=>$name) {
            if (!array_key_exists($code, $subdivision_is)) {
                $subdivision_is[$code] = array();
            }
        }
        
        
        /* Adding data to array */
        $old_measure_code = '';
        foreach ($dataset as $row) {
            list($date, $measure_code, $administrated) = $row;
         
            /* Subdivisions by measure */
            if ($measure_code != $old_measure_code) {
                $subdivisions_by_measure = $measures_subdivisions[strtolower($measure_code)];
                $old_measure_code = $measure_code;
                $reset_previous = true;
                $subdivision_balances = array();
                foreach ($subdivisions as $code=>$name) {
                    $subdivision_balances[$code] = null;
                }
            }
            
            foreach ($subdivisions_by_measure as $subdivision_code=>$time) {
                if ($time > 0) {
                    /* Basic data */
                    $time_groups[$subdivision_code][$date]->sum_administrated += $administrated;
                    $time_groups[$subdivision_code][$date]->sum_time += $administrated * $time;
                    
                    /* Information systems */
                    foreach ($subdivision_is[$subdivision_code] as $is=>$value) {
                        //TODO: same is using sinchorniously
                        $is_time_groups[$is][$date]->sum_n += $administrated;
                        $is_time_groups[$is][$date]->sum_time += $administrated * $time;
                    }
                }
            }
        }
        return $time_groups;
    }
    
    /**
     * @param type $measure_code    measure to group subdivisions by
     * @return array [subdivision_code]=>time
     * 
     * @deprecated use get_measures_subdivisions
     * 
     * @todo subdivisions concerning seasion informations
     */
    public static function get_subdivisions($measure_code, $date = null) {
        $sudivisions = array();
        $measure_code = mysql_escape_string($measure_code);
        $sqlResult = DB::query("SELECT subdivision_code, time FROM support_administration WHERE measure_code = '$measure_code'");
        while (list($subdivision_code, $time) = DB::row($sqlResult)) {
            $sudivisions[$subdivision_code] = $time;
        }
        return $sudivisions;
    }
   
    /**
     * @return [measure_code][subdivision_code] => time
     */
    public static function get_measures_subdivisions() {
        /* Empty matrix */
        $measures = get_support_measures_associative();
        $subdivisions = get_subdivisions_associative();
        $result = array();
        foreach ($measures as $measure=>$value) {
            foreach($subdivisions as $subdivision=>$value) {
                $result[$measure][$subdivision] = 0;
            }
        }
                
        $sqlResult = DB::query("SELECT measure_code, subdivision_code, time FROM support_administration ORDER BY measure_code");
        while ($row = DB::row($sqlResult)) {
            list($measure, $subdivision, $time) = $row;
            //TODO: why strtolower VS get_measures_subdivisions()?
            $result[strtolower($measure)][$subdivision] = floatval($time);
        }
        return $result;
    }
    
     /**
     * @return [measure_code][subdivision_code] => time
     */
    public static function get_measures_subdivisions_by_subdivision_code($subdivision_code) {
        $subdivision_code = mysql_real_escape_string($subdivision_code);
        /* Empty matrix */
        $measures = get_support_measures_associative();
        $subdivisions = get_subdivision_by_code($subdivision_code);
        $result = array();
        foreach ($measures as $measure=>$value) {
            foreach($subdivisions as $subdivision=>$value) {
                $result[$measure][$subdivision] = 0;
            }
        }
                
        $sqlResult = DB::query("SELECT measure_code, subdivision_code, time FROM support_administration WHERE subdivision_code = '".$subdivision_code."' ORDER BY measure_code");
        while ($row = DB::row($sqlResult)) {
            list($measure, $subdivision, $time) = $row;
            //TODO: why strtolower VS get_measures_subdivisions()?
            $result[strtolower($measure)][$subdivision] = floatval($time);
        }
        return $result;
    }
    
    /**
     * @param array $time_groups [subdivision][date]=>TimeGroup
     *              changes are written to given array
     */
    public static function predict(&$time_groups) {
        if (count($time_groups > 0)) {
            /* Getting predicted dates */
            $first_subdivision = reset($time_groups);
            $first_date = null;
            $last_not_predicted = null;
            foreach ($first_subdivision as $date=>$time_group) {
                if (!$first_date) {
                    $first_date = $date;
                }
                if ($time_group->predicted) {
                    $predicted_dates[] = $date;
                } else {
                    $last_not_predicted = $date;
                }
            }
            
            /* Predicting */
            foreach ($time_groups as $subdivision=>$row) {
                $last_date = $last_not_predicted;
                foreach ($predicted_dates as $date) {
                    /* Previous quantities */
                    $previous_time_group = $row[$last_date];

                    /* Manually predicted */
                    if ($row[$date]->sum_time || $row[$date]->sum_administrated) {
                        $row[$date]->predicted_time = $row[$date]->sum_time;
                        $row[$date]->predicted_administrated = $row[$date]->sum_administrated;
                    }
                    
                    /* Counting average */
                    $back_date = $date;
                    $historical_administrated = array();
                    $historical_time = array();
                    $n = 0;
                    do {
                        $back_year = intval(substr($back_date, 0, 4)) - 1;
                        $back_date = $back_year . substr($back_date, 4);
                        if (strtotime($back_date) > strtotime($first_date) && array_key_exists($back_date, $row)) {
                            $historical_administrated[] = $row[$back_date]->sum_administrated();
                            $historical_time[] = $row[$back_date]->sum_time_used();
                            $n++;
                        }
                    } while (strtotime($back_date) > strtotime($first_date));
                    if ($n) {
                        $avg_administrated = array_sum($historical_administrated) / $n;
                        $avg_time = array_sum($historical_time) / $n;
                        $latest_administrated = reset($historical_administrated);
                        $latest_time = reset($historical_time);
                        $addministrated = round(($avg_administrated + $latest_administrated) / 2);
                        $row[$date]->predicted_administrated += $addministrated;
                        $time = round(($avg_time + $latest_time) / 2);
                        $row[$date]->predicted_time += $time;
                    } else {
                        $row[$date]->predicted_administrated += $row[$last_date]->sum_administrated();
                        $row[$date]->predicted_time += $row[$last_date]->sum_time_used();
                    }
                        
                    /* Last date */
                    $last_date = $date;
                }
            }
        }
    }
    
    /**
     * @return array [date]=>booean is predicted
     */
    public static function get_dates_predicted($add_months) {
        $sqlResult = DB::query("SELECT DISTINCT(`date`) FROM historical_quantities ORDER BY `date`");
        $dates = array();
        while (list($date) = DB::row($sqlResult)) {
            $dates[$date] = false;
            $last = $date;
        }
        if ($add_months && isset($last)) {
            $year = date('Y', strtotime($last));
            $month = date('n', strtotime($last));
            for ($i = 0; $i < $add_months; $i++) {
                $month++;
                if ($month > 12) {
                    $month = 1;
                    $year++;
                }
                $month_text = str_pad($month, 2, '0', STR_PAD_LEFT);
                $dates["$year-$month_text-01"] = true;;
            }
        }
        return $dates;
    }
    
    /**
     * @param array $all_dates [date]=>boolean is predicted
     * 
     * @return array [#]=>date
     */
    public static function get_predicted_dates($all_dates) {
        $predicted_dates = array();
        foreach($all_dates as $date=>$is_predicted) {
            if ($is_predicted) {
                $predicted_dates[] = $date;
            }
        }
        return $predicted_dates;
    }
    
    /**
     * @param array $dates              [date]=>predicted
     * @param int   $items_in_width     how many dates per part
     * @param array $time_groups        [serie][date]=>value
     * @param array $new_time_groups    [#][serie][date]=>value
     * @return array [#][date]=>predicted
     */
    public static function divide_dates($dates, $items_in_width, $time_groups, &$new_time_groups) {
        if ($items_in_width === null || count($dates) < $items_in_width) {
            $new_time_groups = array($time_groups);
            return array($dates);
        } else {
            $new_dates = array();
            $n = count($dates);
            $new_time_groups = array();
            $slice_index = 0;
            for($i = 0; $i < $n; $i += $items_in_width) {
                $length = ($i + $items_in_width < $n) ? $items_in_width : $n - $i;
                $dates_slice = array_slice($dates, $i, $length, true);
                $new_dates[] = $dates_slice;
                foreach ($time_groups as $serie=>$row) {
                    foreach ($dates_slice as $date=>$predicted) {
                        $new_time_groups[$slice_index][$serie] = array_slice($row, $i, $length, true);
                    }
                }
                $slice_index++;
            }
            return $new_dates;
        }
    }
}